package com.glc.hms.system.service.impl;


import cn.hutool.cache.Cache;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.glc.hms.common.exception.CoreExceptionEnum;
import com.glc.hms.common.exception.ServiceException;
import com.glc.hms.common.page.PageFactory;
import com.glc.hms.common.util.DateTimeUtils;
import com.glc.hms.core.common.ConstantFactory;
import com.glc.hms.core.shiro.ShiroUser;
import com.glc.hms.core.shiro.ShiroUtil;
import com.glc.hms.core.shiro.SingletonSessionManager;
import com.glc.hms.holder.ShiroSessionHolder;
import com.glc.hms.system.dao.SysUserMapper;
import com.glc.hms.system.entity.SysUser;
import com.glc.hms.system.job.SessionJob.SessionValidate;
import com.glc.hms.system.model.param.UpdatePasswordParam;
import com.glc.hms.system.model.vo.OnlinUserVO;
import com.glc.hms.system.service.ISysRoleService;
import com.glc.hms.system.service.ISysUserService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.IteratorUtils;
import org.apache.el.util.Validation;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;

/**
 * <p>
 * 系统用户 服务实现类
 * </p>
 *
 * @author Santa
 * @since 2019-11-14
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
    private static final String SECRET_SALT = "glc";

    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private ISysRoleService sysRoleService;

    private DefaultWebSessionManager sessionManager = SingletonSessionManager.getInstance();


    @Override
    public Page<Map<String, Object>> queryUserByPage(String type,String keyWords){
        Page page = PageFactory.defaultPage();
        //部门权限
        Long deptId = ShiroUtil.getUserNotNull().getDeptId();
        List<Long> longs = ConstantFactory.me().queryChileDeptId(deptId);
        Page<Map<String, Object>> listPage = sysUserMapper.queryByPage(page,longs,type,keyWords);
        return listPage;
    }



    /**
     * 根据登录名查询
     * @param loginName
     * @return
     */
    @Override
    public SysUser getUserByLoginName(String loginName){
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("login_name", loginName);
        SysUser sysUser = sysUserMapper.selectOne(queryWrapper);
        return sysUser;
    }

    /**
     * 添加用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUser(SysUser user) {
        //判断用户名是否存在
        SysUser oldUser = this.getUserByLoginName(user.getLoginName());
        if (oldUser != null) {
            throw new ServiceException(CoreExceptionEnum.USER_ALREADY_REG);
        }
        // 完善账号信息
        String salt = ShiroUtil.glc_ADLT;
        String password = ShiroUtil.md5(user.getPassword(), salt);
        user.setPassword(password);
        user.setCreateTime(new Date());
        //设置用户初始状态
        user.setEnabled(1);
        this.save(user);
        sysRoleService.addUserRole(user.getUserId(),user.getRoleId());
    }


    /**
     * 修改密码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePassword(UpdatePasswordParam passwordParam) {

        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        // 完善账号信息
        String salt = ShiroUtil.glc_ADLT;
        String password = ShiroUtil.md5(passwordParam.getOldPassword(), salt);
        queryWrapper.eq("user_id",passwordParam.getUserId());
        queryWrapper.eq("password",password);
        SysUser sysUser = sysUserMapper.selectOne(queryWrapper);
        if(sysUser == null){
            throw new ServiceException(500,"旧密码错误!");
        }else {
            String newPassword = ShiroUtil.md5(passwordParam.getNewPassword(), salt);
            sysUser.setPassword(newPassword);
            this.updateById(sysUser);
        }
    }


    /**
     * 获取在线用户列表
     * @return
     */
    @Override
    public Map<String, Object> getOnLineUserList(){
        Map<String, Object> res = Maps.newHashMap();
        Integer onLineUser = SessionValidate.getOnLineUser();
        List<OnlinUserVO> list = Lists.newArrayList();
        Map<Long, Deque<Serializable>> sessions = ShiroSessionHolder.getSessionCache();
        if (sessions == null) return res;
            for (Long userId : sessions.keySet()) {
                Deque<Serializable> deque = sessions.get(userId);
                if(deque.size()>0){
                    Iterator<Serializable> iterator = deque.iterator();
                    while (iterator.hasNext()){
                        Serializable next = iterator.next();
                        try {
                            Session session = sessionManager.getSession(new DefaultSessionKey(next));
                            OnlinUserVO userVO = new OnlinUserVO();
                            SimplePrincipalCollection attribute = (SimplePrincipalCollection)session.getAttribute("org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY");
                            ShiroUser user = (ShiroUser) attribute.getPrimaryPrincipal();
                            userVO.setUserId(userId);
                            userVO.setUserName(user.getUsername());
                            userVO.setLoginAddr(session.getHost());
                            userVO.setDeptName(user.getDeptName());
                            userVO.setSessionId(session.getId().toString());
                            userVO.setLoginTime(DateTimeUtils.convertDateTimeToString(session.getStartTimestamp()));
                            list.add(userVO);
                        } catch (SessionException e) {
                            onLineUser = onLineUser-1;
                        }
                    }
                }
            }

        res.put("total",onLineUser);
        res.put("userList",list);
        res.put("currentSession",ShiroUtil.getSession().getId());
        return res;
    }


    /**
     * 踢人
     * @param sessionId
     */
    @Override
    public void kickoutOnlineUser(String sessionId){
            Map<Long, Deque<Serializable>> sessions = ShiroSessionHolder.getSessionCache();
            if (sessions == null) return;
            for (Long userId : sessions.keySet()) {
                Deque<Serializable> deque = sessions.get(userId);
                if(deque.size()>0){
                    Iterator<Serializable> iterator = deque.iterator();
                    while (iterator.hasNext()){
                        Serializable next = iterator.next();
                        if(sessionId.equals(next)){
                            iterator.remove();
                            log.info("从队列销毁:"+next);
                            try {
                                Session session = sessionManager.getSession(new DefaultSessionKey(next));
                                session.setTimeout(0);
                                log.info("session过期时间设置为0:"+next);
                                return;
                            } catch (Exception e){
                                log.info("This session is expried");
                            }
                        }
                    }
                }
            }
    }

    /**
     * 灭霸模式
     */
    public void kickoutOnlineUserMieba(){
        List<Serializable> sessionIds = ShiroSessionHolder.getSessionCacheList();
        // 移除当前登陆者
        Integer size = sessionIds.size();
            if(size<=1) return;
        sessionIds.remove(ShiroUtil.getSession().getId());
            Set<Serializable> serializables = RandomUtil.randomEleSet(sessionIds, size/2);
            for (Serializable serializable : serializables) {
                this.kickoutOnlineUser(serializable.toString());
            }
        }
}
